#ifndef PANEL_H_
#define PANEL_H_

#include "conf/element.h"
#include "misc.h"
#include "menu.h"

#include "gtkversion.h"

namespace gpyp {
namespace panel {

class PageElement : public conf::Element {
private:
  conf::RefAttributeBool enable_;
  conf::RefAttributeInt  position_;
  conf::RefAttributeUint width_;
  conf::RefAttributeUint height_;

public:
  inline conf::RefAttributeBool enable() {
    return enable_;
  }
  inline conf::RefAttributeInt position() {
    return position_;
  }
  inline conf::RefAttributeUint width() {
    return width_;
  }
  inline conf::RefAttributeUint height() {
    return height_;
  }

protected:
  inline PageElement(const Glib::ustring &name, bool enable) :
    conf::Element(name),
    enable_      ("enable", enable),
    position_    ("position", -1),
    width_       ("width",  200),
    height_      ("height", 200)
  {
    add(enable_);
    add(position_);
    add(width_);
    add(height_);
  }
};
typedef boost::shared_ptr<PageElement> RefPageElement;

class PagePanedElement : public PageElement {
private:
  conf::RefAttributeUint pane_position_;

public:
  inline conf::RefAttributeUint pane_position() {
    return pane_position_;
  }

protected:
  inline PagePanedElement(const Glib::ustring &name, bool enable) :
    PageElement   (name, enable),
    pane_position_("pane_position", 64)
  {
    add(pane_position_);
  }
};
typedef boost::shared_ptr<PagePanedElement> RefPagePanedElement;

class PageBase : boost::noncopyable {
private:
  RefPageElement   conf_;
  sigc::connection conn_conf_presave_;

  Gtk::Widget *widget_;

protected:
  inline virtual void on_presave() {
  }

public:
  template<typename T>
  inline void add(boost::shared_ptr<T> node) {
    conf_->add(node);
  }
  template<typename T>
  inline void remove(boost::shared_ptr<T> node) {
    conf_->remove(node);
  }

  inline RefPageElement get_conf() {
    return conf_;
  }

  inline Gtk::Widget &get_widget() {
    return *widget_;
  }
  inline void set_widget(Gtk::Widget &widget) {
    widget_ = &widget;
  }

protected:
  inline PageBase(RefPageElement conf, Gtk::Widget &widget) :
    conf_  (conf),
    widget_(&widget)
  {
    conn_conf_presave_ = conf_->signal_presave().connect(sigc::mem_fun(*this, &PageBase::on_presave));
  }
public:
  inline virtual ~PageBase() {
    conn_conf_presave_.disconnect();
  }
};

class Paned : boost::noncopyable {
private:
  Gtk::Paned &pane_;

  RefPagePanedElement conf_;

private:
  inline static Gtk::Widget &get_pack_widget(Gtk::Widget &child, bool scrolled_window) {
    if (scrolled_window) {
      Gtk::ScrolledWindow *sw = Gtk::manage(new Gtk::ScrolledWindow());
      sw->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
      sw->add(child);
      return *sw;
    } else {
      return child;
    }
  }

protected:
  inline virtual void on_presave() {
    if (pane_.is_mapped()) conf_->pane_position() = pane_.get_position();
  }

  inline virtual void on_map() {
    pane_.set_position(*conf_->pane_position());
  }
  inline virtual void on_unmap() {
    conf_->pane_position() = pane_.get_position();
  }

  inline void pack1(Gtk::Widget &child, bool resize, bool shrink, bool scrolled_window = false) {
    pane_.pack1(get_pack_widget(child, scrolled_window), resize, shrink);
  }
  inline void pack2(Gtk::Widget &child, bool resize, bool shrink, bool scrolled_window = false) {
    pane_.pack2(get_pack_widget(child, scrolled_window), resize, shrink);
  }

  inline Paned(Gtk::Paned &pane, RefPagePanedElement conf) :
    pane_(pane),
    conf_(conf)
  {
  }
public:
  inline virtual ~Paned() {
  }
};

template<typename T>
class Page : public T,
             public PageBase
{
protected:
  inline Page(RefPageElement conf) :
    PageBase(conf, *this)
  {
  }
  inline Page(Application &app, RefPageElement conf) :
    T       (app),
    PageBase(conf, *this)
  {
  }
};

template<typename T>
class PagePaned : public Page<T>,
                  public Paned {
protected:
  inline virtual void on_presave() {
    Page<T>::on_presave();
    Paned::on_presave();
  }

  inline virtual void on_map() {
    //Page<T>::on_map();
    T::on_map();
    Paned::on_map();
  }
  inline virtual void on_unmap() {
    Paned::on_unmap();
    T::on_unmap();
    //Page<T>::on_unmap();
  }

  inline void pack1(Gtk::Widget &child, bool resize, bool shrink, bool scrolled_window = false) {
    Paned::pack1(child, resize, shrink, scrolled_window);
  }
  inline void pack2(Gtk::Widget &child, bool resize, bool shrink, bool scrolled_window = false) {
    Paned::pack2(child, resize, shrink, scrolled_window);
  }

  inline PagePaned(RefPagePanedElement conf) :
    Page<T>(conf),
    Paned  (*this, conf)
  {
  }
};

class Panel;
class PanelElement : public conf::Element {
private:
  conf::RefAttributeStr    active_page_;
  RefAttributePositionType tab_position_;

public:
  inline conf::RefAttributeStr active_page() {
    return active_page_;
  }
  inline RefAttributePositionType tab_position() {
    return tab_position_;
  }

  inline PanelElement(const Glib::ustring &name) :
    conf::Element(name),
    active_page_ ("active_page",  ""),
    tab_position_("tab_position", Gtk::POS_LEFT)
  {
    add(active_page_);
    add(tab_position_);
  }
};
typedef boost::shared_ptr<PanelElement> RefPanelElement;

class Panel : public Gtk::Notebook {
protected:
  typedef sigc::slot<PageBase *> SlotCreatePanel;
  struct PageContext {
    RefPageElement  conf_;
    SlotCreatePanel slot_create_;
    Glib::ustring   icon_name_;
    Glib::ustring   label_;
    bool            scrolled_window_;
    PageBase       *page_;

    sigc::connection conn_conf_enable_changed_;

    inline PageContext(RefPageElement       conf,
                       SlotCreatePanel      slot_create,
                       const Glib::ustring &icon_name,
                       const Glib::ustring &label,
                       bool                 scrolled_window) :
      conf_           (conf),
      slot_create_    (slot_create),
      icon_name_      (icon_name),
      label_          (label),
      scrolled_window_(scrolled_window),
      page_           (NULL)
    {
    }
    inline ~PageContext() {
      conn_conf_enable_changed_.disconnect();
      if (page_) delete page_;
    }
  };

private:
  Application         &app_;
  RefPanelElement      conf_;
  RefAttributePanelPos position_;
  Gtk::Paned          &paned_;
  bool                 initialized_;

  typedef boost::ptr_list<PageContext> PageList;
  PageList     pages_;
  PageContext *page_selected_;

  Gtk::Menu                 menu_;
  Gtk::Menu                 menu_add_remove_;

  RadioMenuItem<AttributePanelPos> mi_panel_left_;
  RadioMenuItem<AttributePanelPos> mi_panel_right_;
  RadioMenuItem<AttributePanelPos> mi_panel_top_;
  RadioMenuItem<AttributePanelPos> mi_panel_bottom_;
  RadioMenuItem<AttributePanelPos> mi_panel_none_;

  RadioMenuItem<AttributePositionType> mi_tab_left_;
  RadioMenuItem<AttributePositionType> mi_tab_right_;
  RadioMenuItem<AttributePositionType> mi_tab_top_;
  RadioMenuItem<AttributePositionType> mi_tab_bottom_;

#ifdef GTK_NOTEBOOK_TAB_REORDERABLE
  sigc::connection conn_page_reordered_;
#endif
  sigc::connection conn_tab_position_changed_;
  sigc::connection conn_conf_presave_;

private:
  void save_panel_size(RefPageElement conf);
  void load_panel_size(RefPageElement conf);
  void update_height();

  void append_page(PageContext *pc);

  bool init_idle();

protected:
  virtual void on_presave();

  bool realize_idle();
  virtual void on_realize();
  virtual void on_unrealize();
  virtual void on_switch_page(GtkNotebookPage *page, guint page_num);
  virtual bool on_button_press_event(GdkEventButton *event);
#ifdef GTK_NOTEBOOK_TAB_REORDERABLE
  virtual void _on_page_reordered(Gtk::Widget *widget, guint position);
#endif

  void set_label_angle(Gtk::Widget *widget);

  void tab_position_on_changed();

  void page_enable_on_changed(PageContext *pc);

  void install_page(RefPageElement       conf,
                    SlotCreatePanel      slot_create,
                    const Glib::ustring &icon_name,
                    const Glib::ustring &label,
                    bool                 scrolled_window = false);

  PageContext *get_active_page();

  Panel(Application         &app,
        RefPanelElement      conf,
        RefAttributePanelPos position,
        Gtk::Paned          &paned);
  virtual ~Panel();
};

}/*panel*/
}/*gpyp*/

#endif/*PANEL_H_*/
